/****************************************************************************************************************************
  PublishPacket.cpp

  AsyncMQTT_ESP32 is a library for ESP32 boards using WiFi or LwIP W5500 / W6100 / ENC28J60 / LAN8720 Ethernet

  Based on and modified from :

  1) async-mqtt-client (https://github.com/marvinroger/async-mqtt-client)
  2) AsyncMQTT_Generic (https://github.com/khoih-prog/AsyncMQTT_Generic)

  Built by Khoi Hoang https://github.com/khoih-prog/AsyncMQTT_ESP32
 *****************************************************************************************************************************/

#include "PublishPacket.hpp"

using AsyncMqttClientInternals::PublishPacket;

PublishPacket::PublishPacket(ParsingInformation* parsingInformation, OnMessageInternalCallback dataCallback,
                             OnPublishInternalCallback completeCallback)
  : _parsingInformation(parsingInformation)
  , _dataCallback(dataCallback)
  , _completeCallback(completeCallback)
  , _dup(false)
  , _qos(0)
  , _retain(0)
  , _bytePosition(0)
  , _topicLengthMsb(0)
  , _topicLength(0)
  , _ignore(false)
  , _packetIdMsb(0)
  , _packetId(0)
  , _payloadLength(0)
  , _payloadBytesRead(0)
{
  _dup = _parsingInformation->packetFlags & HeaderFlag.PUBLISH_DUP;
  _retain = _parsingInformation->packetFlags & HeaderFlag.PUBLISH_RETAIN;
  char qosMasked = _parsingInformation->packetFlags & 0x06;

  switch (qosMasked)
  {
    case HeaderFlag.PUBLISH_QOS0:
      _qos = 0;
      break;

    case HeaderFlag.PUBLISH_QOS1:
      _qos = 1;
      break;

    case HeaderFlag.PUBLISH_QOS2:
      _qos = 2;
      break;
  }
}

PublishPacket::~PublishPacket()
{
}

void PublishPacket::parseVariableHeader(char* data, size_t len, size_t* currentBytePosition)
{
  (void)len;

  char currentByte = data[(*currentBytePosition)++];

  if (_bytePosition == 0)
  {
    _topicLengthMsb = currentByte;
  }
  else if (_bytePosition == 1)
  {
    _topicLength = currentByte | _topicLengthMsb << 8;

    if (_topicLength > _parsingInformation->maxTopicLength)
    {
      _ignore = true;
    }
    else
    {
      _parsingInformation->topicBuffer[_topicLength] = '\0';
    }
  }
  else if (_bytePosition >= 2 && _bytePosition < 2 + _topicLength)
  {
    // Starting from here, _ignore might be true
    if (!_ignore)
      _parsingInformation->topicBuffer[_bytePosition - 2] = currentByte;

    if (_bytePosition == 2 + _topicLength - 1 && _qos == 0)
    {
      _preparePayloadHandling(_parsingInformation->remainingLength - (_bytePosition + 1));
      return;
    }
  }
  else if (_bytePosition == 2 + _topicLength)
  {
    _packetIdMsb = currentByte;
  }
  else
  {
    _packetId = currentByte | _packetIdMsb << 8;
    _preparePayloadHandling(_parsingInformation->remainingLength - (_bytePosition + 1));
  }

  _bytePosition++;
}

void PublishPacket::_preparePayloadHandling(uint32_t payloadLength)
{
  _payloadLength = payloadLength;

  if (payloadLength == 0)
  {
    _parsingInformation->bufferState = BufferState::NONE;

    if (!_ignore)
    {
      _dataCallback(_parsingInformation->topicBuffer, nullptr, _qos, _dup, _retain, 0, 0, 0, _packetId);
      _completeCallback(_packetId, _qos);
    }
  }
  else
  {
    _parsingInformation->bufferState = BufferState::PAYLOAD;
  }
}

void PublishPacket::parsePayload(char* data, size_t len, size_t* currentBytePosition)
{
  size_t remainToRead = len - (*currentBytePosition);

  if (_payloadBytesRead + remainToRead >
      _payloadLength)
    remainToRead = _payloadLength - _payloadBytesRead;

  if (!_ignore)
    _dataCallback(_parsingInformation->topicBuffer, data + (*currentBytePosition),
                  _qos, _dup, _retain, remainToRead, _payloadBytesRead, _payloadLength, _packetId);

  _payloadBytesRead += remainToRead;
  (*currentBytePosition) += remainToRead;

  if (_payloadBytesRead == _payloadLength)
  {
    _parsingInformation->bufferState = BufferState::NONE;

    if (!_ignore)
      _completeCallback(_packetId, _qos);
  }
}
